package org.yl.ims.util.dbUtil.dialect;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.util.StringUtils;
import org.yl.ims.util.dbUtil.mapping.ColumnMapping;
import org.yl.ims.util.dbUtil.mapping.PrimaryKey;


public abstract class Dialect {
	protected Map<Class<?>, String> defaultJDBCType = new HashMap<Class<?>, String>();
	protected Map<String, Integer> defaultLengthMap = new HashMap<String, Integer>();

	public Dialect() {
		init();
	}

	private void init() {
		defaultJDBCType.put(Integer.class, "Integer");
		defaultJDBCType.put(int.class, "Integer");
		defaultJDBCType.put(Byte.class, "TinyInt");
		defaultJDBCType.put(byte.class, "TinyInt");
		defaultJDBCType.put(Short.class, "SmallInt");
		defaultJDBCType.put(Long.class, "BigInt");
		defaultJDBCType.put(short.class, "SmallInt");
		defaultJDBCType.put(long.class, "BigInt");
		defaultJDBCType.put(String.class, "VARCHAR");
		defaultJDBCType.put(BigDecimal.class, "DECIMAL");
		defaultJDBCType.put(Double.class, "DOUBLE");
		defaultJDBCType.put(Float.class, "FLOAT");
		defaultJDBCType.put(Date.class, "DATE");
		defaultJDBCType.put(byte[].class, "VARBANARY");
		defaultLengthMap.put("VARCHAR", 255);
	}

	public boolean isSimpleProperty(Class<?> clazz) {
		return true;
	}

	public String getDefaultJdbcType(Class<?> clazz) {
		return defaultJDBCType.get(clazz);
	}

	public int getDefaultLength(Class<?> clazz) {
		return 255;
	}

	public static void main(String[] args) {
		Dialect dialect = new MysqlDialect("test");
		System.out.println(dialect.getDefaultJdbcType(Integer.class));
	}

	public String getCreateTableString() {
		return "create table ";
	}

	public String getAlterTableString() {
		return "Alter table ";
	}

	public char openQuote() {
		return '"';
	}

	public char closeQuote() {
		return '"';
	}

	public boolean isTableNameQuoted() {
		return false;
	}

	public boolean isColumnNameQuoted() {
		return false;
	}

	public String quoteTableName(String name) {
		return isTableNameQuoted() ? openQuote() + name + closeQuote() : name;
	}

	public String quoteColumnName(String name) {
		return isColumnNameQuoted() ? openQuote() + name + closeQuote() : name;
	}

	public String quote(String name, JdbcType type) {
		return name;
	}

	public boolean needQuote(String jdbcType) {
		return needQuote(JdbcType.valueOf(jdbcType));
	}

	public boolean needQuote(JdbcType type) {
		return false;
	}

	public String fullTableName(String name) {
		if (getSchema() != null) {
			name = getSchema() + "." + name;
		}
		if (getCatalog() != null) {
			name = getCatalog() + "." + name;
		}
		return name;
	}

	public String nullableString(boolean nullable) {
		return nullable ? "" : " not null ";
	}

	public String columnLength(ColumnMapping column) {
		if (lengthAvailble(column.getJdbcType())) {
			String len = "(" + column.getLength()
					+ (column.getDecimalDigits() > 0 ? "," + column.getDecimalDigits() : "") + ")";
			return len;
		} else {
			return "";
		}
	}

	public boolean lengthAvailble(String jdbcType) {
		return defaultLengthMap.get(jdbcType) != null;
	}

	public String autoGeneratedValue() {
		return " auto_increment ";
	}

	public String getJDBCType(String defaultType) {
		return defaultType;
	}

	public String defaultValue(String defaultvalue, String jdbcType) {
		if (StringUtils.hasText(defaultvalue)) {
			return " default " + (needQuote(jdbcType) ? openQuote() + defaultvalue + closeQuote() : defaultvalue);
		} else {
			return "";
		}
	}

	public String columnString(ColumnMapping column) {
		StringBuffer columndesc = new StringBuffer();
		String c = this.isColumnNameQuoted() ? this.openQuote() + column.getName() + this.closeQuote() : column
				.getName();
		columndesc.append(c).append(" ").append(getJDBCType(column.getJdbcType()));
		columndesc.append(columnLength(column)).append(" ").append(nullableString(column.isNullable()));
		columndesc.append(" ").append(column.isGeneratedValue() ? autoGeneratedValue() : "");
		columndesc.append(" ").append(defaultValue(column.getDefaultValue(), column.getJdbcType()));
		return columndesc.toString();
	}

	public String primaryKeyString(PrimaryKey pk) {
		if (pk == null || pk.getProperties().size() == 0) {
			return "";
		} else {
			StringBuffer pkdesc = new StringBuffer();
			if (StringUtils.hasText(pk.getName())) {
				pkdesc.append("CONSTRAINT ").append(pk.getName()).append(" ");
			}
			pkdesc.append("PRIMARY KEY (");
			for (String p : pk.getColumns()) {
				pkdesc.append(quoteColumnName(p)).append(",");
			}
			pkdesc.setLength(pkdesc.length() - 1);
			pkdesc.append(")");
			return pkdesc.toString();
		}
	}

	public abstract String getCatalog();

	public abstract String getSchema();

	public static Dialect getDialect(Connection conn) throws SQLException {
		DatabaseMetaData meta = conn.getMetaData();
		String dbname = meta.getDatabaseProductName();
		if (dbname.toLowerCase().indexOf("mysql") >= 0) {
			String db = null;
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery("select database() db");
			if (rs.next()) {
				db = rs.getString("db");
			}
			rs.close();
			stmt.close();
			return new MysqlDialect(db);
		} else if (dbname.toLowerCase().indexOf("oracle") >= 0) {
			String user = meta.getUserName();
			return new OracleDialect(user);
		} else if (dbname.toLowerCase().indexOf("sqlite") >= 0) {
			return new SqliteDialect();
		} else if (dbname.toLowerCase().indexOf("postgresql") >= 0) {
			return new PostgreSQLDialect();
		}else {
			throw new IllegalArgumentException(dbname + " is not support!");
		}
	}
}
